/*
 *  aplay.c - plays and records
 *
 *      CREATIVE LABS CHANNEL-files
 *      Microsoft WAVE-files
 *      SPARC AUDIO .AU-files
 *      Raw Data
 *
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *  Based on vplay program by Michael Beck
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <locale.h>
#include <alsa/asoundlib.h>
#include <assert.h>
#include <termios.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <endian.h>
//#include "aconfig.h"
//#include "gettext.h"
//#include "formats.h"
//#include "version.h"
//add by chenli
#include <pthread.h>
#include <ortp/ortp.h>
#include "common_def.h"

#ifndef LLONG_MAX
#define LLONG_MAX    9223372036854775807LL
#endif

#ifndef le16toh
#include <asm/byteorder.h>
#define le16toh(x) __le16_to_cpu(x)
#define be16toh(x) __be16_to_cpu(x)
#define le32toh(x) __le32_to_cpu(x)
#define be32toh(x) __be32_to_cpu(x)
#endif

#define DEFAULT_FORMAT		SND_PCM_FORMAT_U8
#define DEFAULT_SPEED 		8000

#define FORMAT_DEFAULT		-1
#define FORMAT_RAW		0
#define FORMAT_VOC		1
#define FORMAT_WAVE		2
#define FORMAT_AU		3

#define debug_print printf("file:%s,func:%s,line:%d\n",__FILE__,__func__,__LINE__);
/* global data */

static snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);
static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, snd_pcm_uframes_t size);
static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);

enum {
	VUMETER_NONE,
	VUMETER_MONO,
	VUMETER_STEREO
};

static char *command;
static snd_pcm_t *handle;
static snd_pcm_t *handle1;
static struct audio_pcm_info{
	snd_pcm_format_t format;
	unsigned int channels;
	unsigned int rate;
} hwparams, rhwparams;

struct audio_pcm_data_info
{
	char *command;
	int timelimit;
	u_char *audiobuf;
	char pcm_name[10];
	snd_pcm_uframes_t chunk_size;
	snd_pcm_info_t *info;
	snd_output_t *log;

	snd_pcm_t *handle;
	unsigned period_time;
	unsigned buffer_time;
	snd_pcm_uframes_t period_frames;
	snd_pcm_uframes_t buffer_frames;
	snd_pcm_stream_t stream;

	int quiet_mode;
	int can_pause;
	int buffer_pos;
	int avail_min ;
	int start_delay ;
	int stop_delay ;
	int monotonic ;
	int interactive ;
	int interleaved;
	int file_type;
	
	size_t bits_per_sample;
	size_t bits_per_frame;
	size_t chunk_bytes;
	int test_position;
	int test_coef;
	int test_nowait;
	long long max_file_size;
	int max_file_time;
	int use_strftime;
	volatile int recycle_capture_file;
	long term_c_lflag ;
	int dump_hw_params;
	int fd ;
	int open_mode;
	int nonblock;
	int vocmajor;
	int vocminor;
	int verbose;
	int vumeter;

	off64_t pbrec_count;
	off64_t fdcount;
	struct audio_pcm_info hwparams;	
	struct audio_pcm_info rhwparams;	
};


struct audio_pcm_data_info *g_p_info;

static int timelimit = 0;
static int file_type = FORMAT_DEFAULT;
static int open_mode = 0;
static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
static int mmap_flag = 0;
static int interleaved = 1;
static int nonblock = 0;
static u_char *audiobuf = NULL;
static u_char *audiobuf1 = NULL;
static snd_pcm_uframes_t chunk_size = 0;
static unsigned period_time = 0;
static unsigned buffer_time = 0;
static snd_pcm_uframes_t period_frames = 0;
static snd_pcm_uframes_t buffer_frames = 0;
static int avail_min = -1;
static int start_delay = 0;
static int stop_delay = 0;
static int monotonic = 0;
static int interactive = 0;
static int can_pause = 0;
static int fatal_errors = 0;
static int verbose = 0;
static int vumeter = VUMETER_NONE;
static int buffer_pos = 0;
static size_t bits_per_sample, bits_per_frame;
static size_t chunk_bytes;
static int test_position = 0;
static int test_coef = 8;
static int test_nowait = 0;
static snd_output_t *log;
static long long max_file_size = 0;
static int max_file_time = 0;
static int use_strftime = 0;
volatile static int recycle_capture_file = 0;
static long term_c_lflag = -1;
static int dump_hw_params = 0;

static int fd = -1;
static off64_t pbrec_count = LLONG_MAX, fdcount;
static int vocmajor, vocminor;

static char *pidfile_name = NULL;
FILE *pidf = NULL;
static int pidfile_written = 0;

/* needed prototypes */

static void done_stdin(void);

static void playback(char *filename);



/*
 *	Subroutine to clean up before exit.
 */
static void prg_exit(int code) 
{
	fprintf(stderr,"clear program cache\n");
	done_stdin();
	if (g_p_info->handle)
		snd_pcm_close(g_p_info->handle);
	snd_config_update_free_global();
	if (pidfile_written)
		remove (pidfile_name);
	exit(code);
}

static void signal_handler(int sig)
{
	static int in_aborting;

	if (in_aborting)
		return;

	in_aborting = 1;
	if (verbose==2)
		putchar('\n');
	if (fd > 1) {
		close(fd);
		fd = -1;
	}
	if (g_p_info->handle && sig != SIGABRT) {
		snd_pcm_close(g_p_info->handle);
		g_p_info->handle = NULL;
	}
	

	prg_exit(EXIT_FAILURE);
}

/* call on SIGUSR1 signal. */
static void signal_handler_recycle (int sig)
{
	/* flag the capture loop to start a new output file */
	recycle_capture_file = 1;
}



	pthread_t thread[2];  
	pthread_mutex_t mut; 

static ssize_t pcm_write(u_char *data, size_t count);
static ssize_t safe_read(int fd, void *buf, size_t count);
static ssize_t pcm_write_data(struct audio_pcm_data_info *p_info, size_t count);
void set_pcm_params(struct audio_pcm_data_info *p_info);



struct audio_pcm_data_info *pcm_play_init(char *name)
{
	struct audio_pcm_data_info *p_info;
	
	int err;

	p_info = (struct audio_pcm_data_info *)malloc(sizeof(struct audio_pcm_data_info));
	if(p_info == NULL)
		{
			printf("malloc fail \n");
			return NULL;
		}

	memset(p_info,0,sizeof(struct audio_pcm_data_info));
	memcpy(p_info->pcm_name,"default",sizeof("default"));
	snd_pcm_info_alloca(&p_info->info);

	err = snd_output_stdio_attach(&p_info->log, stderr, 0);
	assert(err >= 0);	

	p_info->stream = SND_PCM_STREAM_PLAYBACK;
	p_info->command = "aplay";
	p_info->file_type = FORMAT_DEFAULT;
	p_info->chunk_size = -1;
	p_info->rhwparams.format = DEFAULT_FORMAT;
	p_info->rhwparams.rate = DEFAULT_SPEED;
	p_info->rhwparams.channels = 1;
	p_info->interleaved = 1;
	p_info->avail_min = -1;
	p_info->vumeter = VUMETER_NONE;
	p_info->pbrec_count = LLONG_MAX;


	
	err = snd_pcm_open(&p_info->handle, p_info->pcm_name, p_info->stream, p_info->open_mode);
	if (err < 0) {
		return NULL;
	}

	if ((err = snd_pcm_info(p_info->handle,p_info-> info)) < 0) {
		return NULL;
	}

	if (p_info->nonblock) {
		err = snd_pcm_nonblock(p_info->handle, 1);
		if (err < 0) {
			return NULL;
		}
	}

	p_info->chunk_size = 1024;
	p_info->hwparams.rate = 44100;
	p_info->hwparams.format = SND_PCM_FORMAT_S16_LE;
	p_info->hwparams.channels = 2;

	p_info->audiobuf = (u_char *)malloc(1024);
	if (p_info->audiobuf == NULL) {
		return NULL;
	}

	if ((p_info->fd = open(name, O_RDONLY, 0)) == -1) {
			perror(name);
			prg_exit(EXIT_FAILURE);
		}

	printf("name:%s,p_info->fd:%d\n",name, p_info->fd);
	return p_info;
	

}



RtpSession *session;


void ssrc_cb(RtpSession *session)
{
	printf("hey, the ssrc has changed !\n");
}

void test_ortp_init(char *port)
{

	int jittcomp=40;
	bool_t adapt=TRUE;

	printf("port:%s\n",port);
	ortp_init();
	ortp_scheduler_init();
	ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
	rtp_profile_set_payload(&av_profile,35,&payload_type_wav_44100);
	session=rtp_session_new(RTP_SESSION_RECVONLY);	
	rtp_session_set_scheduling_mode(session,1);
	rtp_session_set_blocking_mode(session,1);
	rtp_session_set_local_addr(session,"0.0.0.0",atoi(port));
	rtp_session_set_connected_mode(session,TRUE);
	rtp_session_set_symmetric_rtp(session,TRUE);
	rtp_session_enable_adaptive_jitter_compensation(session,adapt);
	rtp_session_set_jitter_compensation(session,jittcomp);
	rtp_session_set_payload_type(session,35);
	rtp_session_signal_connect(session,"ssrc_changed",(RtpCallback)ssrc_cb,0);
	rtp_session_signal_connect(session,"ssrc_changed",(RtpCallback)rtp_session_reset,0);

	
}

void *send_thread()
{
	do{
	sleep(2);
	printf("thread send\n");
	}while(0);
}

void *receive_thread()
{

	int l, r;
	off64_t written = 0;
	off64_t c;
	off64_t count = LLONG_MAX;
	size_t loaded = 0;

	int have_more;
	unsigned int ts;
	int err;
	char tmp_buff[1024];
	char receive_over = 0;
	struct audio_pcm_data_info *p_info;
	p_info = pcm_play_init("123.wav");
	g_p_info = p_info;
	set_pcm_params(p_info);


	
	//sleep(20);
	if (written > 0 && loaded > 0)
		memmove(p_info->audiobuf, p_info->audiobuf + written, loaded);
	printf("read data count:%ld\n",count);
	l = loaded;
#if 0
	while (written < count) {
		do {
			have_more=1;

			c = count - written;
			if (c > p_info->chunk_bytes)
				c = p_info->chunk_bytes;
			c -= l;

			if (c == 0)
				break;
			r = safe_read(p_info->fd, p_info->audiobuf + l, c);
			//err=rtp_session_recv_with_ts(session,buffer,160,ts,&have_more);
			
			//debug_print
			if (r < 0) {
				prg_exit(EXIT_FAILURE);
			}
			p_info->fdcount += r;
			if (r == 0)
				break;
			l += r;
		} while ((size_t)l < p_info->chunk_bytes);
		//debug_print
			printf("write data:%d\n",l);
		l = l * 8 / p_info->bits_per_frame;
		r = pcm_write_data(p_info, l);
		if ((r != l)||(l == 0))
			break;
		r = r *p_info->bits_per_frame / 8;
		written += r;
		l = 0;
	}

#endif

#if 1
l=0;
while (1) {
		do {
			have_more=1;
			//r = safe_read(p_info->fd, p_info->audiobuf + l, c);
			while(have_more)
				{
					err=rtp_session_recv_with_ts(session,tmp_buff,512,ts,&have_more);
					if(err>0)
						{
						//printf("ts:%d,err:%d,have_more:%d\n",ts,err,have_more);
							memcpy(p_info->audiobuf+r,tmp_buff,err);
							r+=err;
							receive_over= 0;				
						}
					else 
						{
							if(receive_over>= 10)
								{
									break;
								}
							receive_over++;
						}

				}
					
			l = r;
			ts+=512;

		} while ((size_t)l < p_info->chunk_bytes);
		//debug_print
			if(receive_over >= 10)
				break;
		l = l * 8 / p_info->bits_per_frame;
		r = pcm_write_data(p_info, l);
	
		r = r *p_info->bits_per_frame / 8;
		l = 0;
		r = 0;
	}

#endif
	
	snd_pcm_nonblock(p_info->handle, 0);
	snd_pcm_drain(p_info->handle);
	snd_pcm_nonblock(p_info->handle, p_info->nonblock);

	rtp_session_destroy(session);
	ortp_exit();
	
	ortp_global_stats_display();

	while(1)
		{
		sleep(3);
		printf("\n");
	}

}



int main(int argc, char *argv[])
{


		writei_func = snd_pcm_writei;
		readi_func = snd_pcm_readi;
		writen_func = snd_pcm_writen;
		readn_func = snd_pcm_readn;



	signal(SIGINT, signal_handler);
	signal(SIGTERM, signal_handler);
	signal(SIGABRT, signal_handler);
	signal(SIGUSR1, signal_handler_recycle);
	
	test_ortp_init(argv[1]);

	int temp;
	memset(&thread, 0, sizeof(thread));
	if((temp = pthread_create(&thread[0], NULL, send_thread, NULL)) != 0)         
		printf("start a new thread!\n");  
	else  
		printf("start new thread fail\n");  
	if((temp = pthread_create(&thread[1], NULL, receive_thread, NULL)) != 0)    
		printf("start a new thread 2\n");  
	else  
		printf("start a new thread fail\n");  
	
    while(1)
		sleep(100);
	snd_config_update_free_global();
	prg_exit(EXIT_SUCCESS);
	/* avoid warning */
	return EXIT_SUCCESS;
}

/*
 * Safe read (for pipes)
 */
 
static ssize_t safe_read(int fd, void *buf, size_t count)
{
	ssize_t result = 0, res;

	while (count > 0) {
		if ((res = read(fd, buf, count)) == 0)
			break;
		if (res < 0)
			return result > 0 ? result : res;
		count -= res;
		result += res;
		buf = (char *)buf + res;
	}
	return result;
}







void set_pcm_params(struct audio_pcm_data_info *p_info)
{
	snd_pcm_hw_params_t *params;
	snd_pcm_sw_params_t *swparams;
	snd_pcm_uframes_t buffer_size;
	int err;
	size_t n;
	unsigned int rate;
	snd_pcm_uframes_t start_threshold, stop_threshold;
	snd_pcm_hw_params_alloca(&params);
	snd_pcm_sw_params_alloca(&swparams);
	err = snd_pcm_hw_params_any(p_info->handle, params);
	if (err < 0) {
		prg_exit(EXIT_FAILURE);
	}
	//p_info->hwparams = *audio_pcm_info_0;
	//if (dump_hw_params) {
	if(1){
			snd_pcm_name(p_info->handle);
		snd_pcm_hw_params_dump(params, p_info->log);
	}
	
	 if (p_info->interleaved)
		err = snd_pcm_hw_params_set_access(p_info->handle, params,
						   SND_PCM_ACCESS_RW_INTERLEAVED);
	else
		err = snd_pcm_hw_params_set_access(p_info->handle, params,
						   SND_PCM_ACCESS_RW_NONINTERLEAVED);
	if (err < 0) {
		printf("Access type not available");
		prg_exit(EXIT_FAILURE);
	}
	err = snd_pcm_hw_params_set_format(p_info->handle, params, p_info->hwparams.format);
	if (err < 0) {
		printf("Sample format non available");
		//show_available_sample_formats(params);
		prg_exit(EXIT_FAILURE);
	}
	debug_print
	err = snd_pcm_hw_params_set_channels(p_info->handle, params, p_info->hwparams.channels);
	if (err < 0) {
		error("Channels count non available");
		prg_exit(EXIT_FAILURE);
	}

#if 0
	err = snd_pcm_hw_params_set_periods_min(handle, params, 2);
	assert(err >= 0);
#endif
	debug_print
	rate = p_info->hwparams.rate;
	err = snd_pcm_hw_params_set_rate_near(p_info->handle, params, &p_info->hwparams.rate, 0);
	assert(err >= 0);
	if ((float)rate * 1.05 < p_info->hwparams.rate || (float)rate * 0.95 > p_info->hwparams.rate) {
		if (!p_info->quiet_mode) {
			char plugex[64];
			const char *pcmname = snd_pcm_name(p_info->handle);
			if (! pcmname || strchr(snd_pcm_name(p_info->handle), ':'))
				*plugex = 0;
					}
	}
	rate = p_info->hwparams.rate;
	debug_print
	if (p_info->buffer_time == 0 && p_info->buffer_frames == 0) {
		err = snd_pcm_hw_params_get_buffer_time_max(params,
							    &p_info->buffer_time, 0);
		assert(err >= 0);
		if (p_info->buffer_time > 500000)
			p_info->buffer_time = 500000;
	}
	if (p_info->period_time == 0 && p_info->period_frames == 0) {
		if (p_info->buffer_time > 0)
			p_info->period_time = p_info->buffer_time / 4;
		else
			p_info->period_frames = p_info->buffer_frames / 4;
	}
	if (p_info->period_time > 0)
		err = snd_pcm_hw_params_set_period_time_near(p_info->handle, params,
							     &p_info->period_time, 0);
	else
		err = snd_pcm_hw_params_set_period_size_near(p_info->handle, params,
							     &p_info->period_frames, 0);
	assert(err >= 0);
	if (p_info->buffer_time > 0) {
		err = snd_pcm_hw_params_set_buffer_time_near(p_info->handle, params,
							     &p_info->buffer_time, 0);
	} else {
		err = snd_pcm_hw_params_set_buffer_size_near(p_info->handle, params,
							     &p_info->buffer_frames);
	}
	debug_print
	assert(err >= 0);
	p_info->monotonic = snd_pcm_hw_params_is_monotonic(params);
	p_info->can_pause = snd_pcm_hw_params_can_pause(params);
	err = snd_pcm_hw_params(p_info->handle, params);
	if (err < 0) {
		printf("Unable to install hw params:");
		snd_pcm_hw_params_dump(params, p_info->log);
		prg_exit(EXIT_FAILURE);
	}
	snd_pcm_hw_params_get_period_size(params, &p_info->chunk_size, 0);
	snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
	if (p_info->chunk_size == buffer_size) {
		printf(("Can't use period equal to buffer size (%lu == %lu)"),
		      p_info->chunk_size, buffer_size);
		prg_exit(EXIT_FAILURE);
	}
	snd_pcm_sw_params_current(p_info->handle, swparams);
	if (p_info->avail_min < 0)
		n = p_info->chunk_size;
	else
		n = (double) rate * p_info->avail_min / 1000000;
	err = snd_pcm_sw_params_set_avail_min(p_info->handle, swparams, n);

	debug_print
	/* round up to closest transfer boundary */
	n = buffer_size;
	if (p_info->start_delay <= 0) {
		start_threshold = n + (double) rate * p_info->start_delay / 1000000;
	} else
		start_threshold = (double) rate * p_info->start_delay / 1000000;
	if (start_threshold < 1)
		start_threshold = 1;
	if (start_threshold > n)
		start_threshold = n;
	err = snd_pcm_sw_params_set_start_threshold(p_info->handle, swparams, start_threshold);
	assert(err >= 0);
	if (p_info->stop_delay <= 0) 
		stop_threshold = buffer_size + (double) rate * p_info->stop_delay / 1000000;
	else
		stop_threshold = (double) rate * p_info->stop_delay / 1000000;
	err = snd_pcm_sw_params_set_stop_threshold(p_info->handle, swparams, stop_threshold);
	assert(err >= 0);

	if (snd_pcm_sw_params(p_info->handle, swparams) < 0) {
		printf(("unable to install sw params:"));
		snd_pcm_sw_params_dump(swparams, p_info->log);
		prg_exit(EXIT_FAILURE);
	}

	if (p_info->verbose)
		snd_pcm_dump(p_info->handle, p_info->log);

	p_info->bits_per_sample = snd_pcm_format_physical_width(p_info->hwparams.format);
	p_info->bits_per_frame = p_info->bits_per_sample * p_info->hwparams.channels;
	p_info->chunk_bytes = p_info->chunk_size * p_info->bits_per_frame / 8;
	p_info->audiobuf= realloc(p_info->audiobuf, p_info->chunk_bytes);
	if (p_info->audiobuf == NULL) {
		printf(("not enough memory"));
		prg_exit(EXIT_FAILURE);
	}
	// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);

	/* stereo VU-meter isn't always available... */
	if (p_info->vumeter == VUMETER_STEREO) {
		if (p_info->hwparams.channels != 2 || !p_info->interleaved || p_info->verbose > 2)
			p_info->vumeter = VUMETER_MONO;
	}

	debug_print
	p_info->buffer_frames = buffer_size;	/* for position test */
}




static void init_stdin(void)
{
	struct termios term;
	long flags;

	if (!interactive)
		return;
	tcgetattr(fileno(stdin), &term);
	term_c_lflag = term.c_lflag;
	if (fd == fileno(stdin))
		return;
	flags = fcntl(fileno(stdin), F_GETFL);
	term.c_lflag &= ~ICANON;
	tcsetattr(fileno(stdin), TCSANOW, &term);
}

static void done_stdin(void)
{
	struct termios term;

	if (!interactive)
		return;
	if (fd == fileno(stdin) || term_c_lflag == -1)
		return;
	tcgetattr(fileno(stdin), &term);
	term.c_lflag = term_c_lflag;
	tcsetattr(fileno(stdin), TCSANOW, &term);
}

static void do_pause(void)
{
	int err;
	unsigned char b;

	if (!can_pause) {
		return;
	}
	err = snd_pcm_pause(handle, 1);
	if (err < 0) {
		printf(("pause push error: %s"), snd_strerror(err));
		return;
	}
	while (1) {
		while (read(fileno(stdin), &b, 1) != 1);
		if (b == ' ' || b == '\r') {
			while (read(fileno(stdin), &b, 1) == 1);
			err = snd_pcm_pause(handle, 0);
			if (err < 0)
				printf(("pause release error: %s"), snd_strerror(err));
			return;
		}
	}
}

static void check_stdin(void)
{
	unsigned char b;

	if (!interactive)
		return;
	if (fd != fileno(stdin)) {
		while (read(fileno(stdin), &b, 1) == 1) {
			if (b == ' ' || b == '\r') {
				while (read(fileno(stdin), &b, 1) == 1);
				fflush(stderr);
			do_pause();
				fflush(stderr);
			}
		}
	}
}

#ifndef timersub
#define	timersub(a, b, result) \
do { \
	(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
	(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
	if ((result)->tv_usec < 0) { \
		--(result)->tv_sec; \
		(result)->tv_usec += 1000000; \
	} \
} while (0)
#endif

#ifndef timermsub
#define	timermsub(a, b, result) \
do { \
	(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
	(result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \
	if ((result)->tv_nsec < 0) { \
		--(result)->tv_sec; \
		(result)->tv_nsec += 1000000000L; \
	} \
} while (0)
#endif

/* I/O error handler */
static void xrun(void)
{
	snd_pcm_status_t *status;
	int res;
	
	snd_pcm_status_alloca(&status);
	if ((res = snd_pcm_status(g_p_info->handle, status))<0) {
		printf(("status error: %s"), snd_strerror(res));
		prg_exit(EXIT_FAILURE);
	}
	if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
		if (fatal_errors) {
			printf(("fatal %s: %s"),
					stream == SND_PCM_STREAM_PLAYBACK ? ("underrun") : ("overrun"),
					snd_strerror(res));
			prg_exit(EXIT_FAILURE);
		}
		if (monotonic) {
#ifdef HAVE_CLOCK_GETTIME
			struct timespec now, diff, tstamp;
			clock_gettime(CLOCK_MONOTONIC, &now);
			snd_pcm_status_get_trigger_htstamp(status, &tstamp);
			timermsub(&now, &tstamp, &diff);
				stream == SND_PCM_STREAM_PLAYBACK ? ("underrun") : ("overrun"),
				diff.tv_sec * 1000 + diff.tv_nsec / 10000000.0);
#else
#endif
		} else {
			struct timeval now, diff, tstamp;
			gettimeofday(&now, 0);
			snd_pcm_status_get_trigger_tstamp(status, &tstamp);
			timersub(&now, &tstamp, &diff);
				
		}
		if (verbose) {
			snd_pcm_status_dump(status, log);
		}
		if ((res = snd_pcm_prepare(g_p_info->handle))<0) {
			printf(("xrun: prepare error: %s"), snd_strerror(res));
			prg_exit(EXIT_FAILURE);
		}
		return;		/* ok, data should be accepted again */
	} if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) {
		if (verbose) {
			snd_pcm_status_dump(status, log);
		}
		if (stream == SND_PCM_STREAM_CAPTURE) {
			if ((res = snd_pcm_prepare(g_p_info->handle))<0) {
				printf(("xrun(DRAINING): prepare error: %s"), snd_strerror(res));
				prg_exit(EXIT_FAILURE);
			}
			return;
		}
	}
	if (verbose) {
		snd_pcm_status_dump(status, log);
	}
	printf(("read/write error, state = %s"), snd_pcm_state_name(snd_pcm_status_get_state(status)));
	prg_exit(EXIT_FAILURE);
}

/* I/O suspend handler */
static void suspend(void)
{
	int res;

	while ((res = snd_pcm_resume(handle)) == -EAGAIN)
		sleep(1);	/* wait until suspend flag is released */
	if (res < 0) {
		
		if ((res = snd_pcm_prepare(handle)) < 0) {
			printf(("suspend: prepare error: %s"), snd_strerror(res));
			prg_exit(EXIT_FAILURE);
		}
	}
	
}



/* peak handler */
static void compute_max_peak(u_char *data, size_t count)
{
	signed int val, max, perc[2], max_peak[2];
	static	int	run = 0;
	size_t ocount = count;
	int	format_little_endian = snd_pcm_format_little_endian(hwparams.format);	
	int ichans, c;

	if (vumeter == VUMETER_STEREO)
		ichans = 2;
	else
		ichans = 1;

	memset(max_peak, 0, sizeof(max_peak));
	switch (bits_per_sample) {
	case 8: {
		signed char *valp = (signed char *)data;
		signed char mask = snd_pcm_format_silence(hwparams.format);
		c = 0;
		while (count-- > 0) {
			val = *valp++ ^ mask;
			val = abs(val);
			if (max_peak[c] < val)
				max_peak[c] = val;
			if (vumeter == VUMETER_STEREO)
				c = !c;
		}
		break;
	}
	case 16: {
		signed short *valp = (signed short *)data;
		signed short mask = snd_pcm_format_silence_16(hwparams.format);
		signed short sval;

		count /= 2;
		c = 0;
		while (count-- > 0) {
			if (format_little_endian)
				sval = le16toh(*valp);
			else
				sval = be16toh(*valp);
			sval = abs(sval) ^ mask;
			if (max_peak[c] < sval)
				max_peak[c] = sval;
			valp++;
			if (vumeter == VUMETER_STEREO)
				c = !c;
		}
		break;
	}
	case 24: {
		unsigned char *valp = data;
		signed int mask = snd_pcm_format_silence_32(hwparams.format);

		count /= 3;
		c = 0;
		while (count-- > 0) {
			if (format_little_endian) {
				val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
			} else {
				val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
			}
			/* Correct signed bit in 32-bit value */
			if (val & (1<<(bits_per_sample-1))) {
				val |= 0xff<<24;	/* Negate upper bits too */
			}
			val = abs(val) ^ mask;
			if (max_peak[c] < val)
				max_peak[c] = val;
			valp += 3;
			if (vumeter == VUMETER_STEREO)
				c = !c;
		}
		break;
	}
	case 32: {
		signed int *valp = (signed int *)data;
		signed int mask = snd_pcm_format_silence_32(hwparams.format);

		count /= 4;
		c = 0;
		while (count-- > 0) {
			if (format_little_endian)
				val = le32toh(*valp);
			else
				val = be32toh(*valp);
			val = abs(val) ^ mask;
			if (max_peak[c] < val)
				max_peak[c] = val;
			valp++;
			if (vumeter == VUMETER_STEREO)
				c = !c;
		}
		break;
	}
	default:
		if (run == 0) {
			run = 1;
		}
		return;
	}
	max = 1 << (bits_per_sample-1);
	if (max <= 0)
		max = 0x7fffffff;

	for (c = 0; c < ichans; c++) {
		if (bits_per_sample > 16)
			perc[c] = max_peak[c] / (max / 100);
		else
			perc[c] = max_peak[c] * 100 / max;
	}

	if (interleaved && verbose <= 2) {
		static int maxperc[2];
		static time_t t=0;
		const time_t tt=time(NULL);
		if(tt>t) {
			t=tt;
			maxperc[0] = 0;
			maxperc[1] = 0;
		}
		for (c = 0; c < ichans; c++)
			if (perc[c] > maxperc[c])
				maxperc[c] = perc[c];

		putc('\r', stderr);
		fflush(stderr);
	}
	else if(verbose==3) {
		for (val = 0; val < 20; val++)
			if (val <= perc[0] / 5)
				putchar('#');
			else
				putchar(' ');
		fflush(stderr);
	}
}

static void do_test_position(void)
{
	static long counter = 0;
	static time_t tmr = -1;
	time_t now;
	static float availsum, delaysum, samples;
	static snd_pcm_sframes_t maxavail, maxdelay;
	static snd_pcm_sframes_t minavail, mindelay;
	static snd_pcm_sframes_t badavail = 0, baddelay = 0;
	snd_pcm_sframes_t outofrange;
	snd_pcm_sframes_t avail, delay;
	int err;

	err = snd_pcm_avail_delay(handle, &avail, &delay);
	if (err < 0)
		return;
	outofrange = (test_coef * (snd_pcm_sframes_t)buffer_frames) / 2;
	if (avail > outofrange || avail < -outofrange ||
	    delay > outofrange || delay < -outofrange) {
	  badavail = avail; baddelay = delay;
	  availsum = delaysum = samples = 0;
	  maxavail = maxdelay = 0;
	  minavail = mindelay = buffer_frames * 16;

	} else if (verbose) {
		time(&now);
		if (tmr == (time_t) -1) {
			tmr = now;
			availsum = delaysum = samples = 0;
			maxavail = maxdelay = 0;
			minavail = mindelay = buffer_frames * 16;
		}
		if (avail > maxavail)
			maxavail = avail;
		if (delay > maxdelay)
			maxdelay = delay;
		if (avail < minavail)
			minavail = avail;
		if (delay < mindelay)
			mindelay = delay;
		availsum += avail;
		delaysum += delay;
		samples++;
		if (avail != 0 && now != tmr) {
		
			tmr = now;
		}
	}
}

/*
 *  write function
 */
static ssize_t pcm_write_data(struct audio_pcm_data_info * p_info, size_t count)
{
	ssize_t r;
	ssize_t result = 0;
	u_char *data = p_info->audiobuf;
		//printf("count:%d\n",count);
	//sleep(1);
	while (count > 0) {
		r = writei_func(p_info->handle, data, count);
		if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
				debug_print
				snd_pcm_wait(p_info->handle, 100);
		} else if (r == -EPIPE) {
			debug_print
			xrun();
		} else if (r == -ESTRPIPE) {
			debug_print
			suspend();
		} else if (r < 0) {
			printf(("write error: %s"), snd_strerror(r));
			prg_exit(EXIT_FAILURE);
		}
		if (r > 0) {
			debug_print
			//if (p_info->vumeter)
				//compute_max_peak(p_info->audiobuf, r * p_info->hwparams.channels);
			result += r;
			count -= r;
			data += r * p_info->bits_per_frame / 8;
		}
	}
	return result;
}



/*
 *  read function
 */


/*
 *  ok, let's play a .voc file
 */


/* setting the globals for playing raw data */
static void init_raw_data(void)
{
	hwparams = rhwparams;
}

/* calculate the data count to read from/to dsp */
static off64_t calc_count(void)
{
	off64_t count;

	if (timelimit == 0) {
		count = pbrec_count;
	} else {
		count = snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
		count *= (off64_t)timelimit;
	}
	return count < pbrec_count ? count : pbrec_count;
}





/* playing raw data */
#if 0
static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *name)
{
	int l, r;
	off64_t written = 0;
	off64_t c;

	debug_print
	//header(rtype, name);
	//set_params();
	while (loaded > chunk_bytes && written < count) {
		if (pcm_write(audiobuf + written, chunk_size) <= 0)
			return;
		written += chunk_bytes;
		loaded -= chunk_bytes;
	}

	sleep(20);
	if (written > 0 && loaded > 0)
		memmove(audiobuf, audiobuf + written, loaded);
	//printf("read data count:%d\n",count);
	l = loaded;
	while (written < count) {
		do {
			c = count - written;
			if (c > chunk_bytes)
				c = chunk_bytes;
			c -= l;

			if (c == 0)
				break;
			r = safe_read(fd, audiobuf + l, c);
			if (r < 0) {
				perror(name);
				prg_exit(EXIT_FAILURE);
			}
			fdcount += r;
			if (r == 0)
				break;
			l += r;
		} while ((size_t)l < chunk_bytes);
		l = l * 8 / bits_per_frame;
		r = pcm_write(audiobuf, l);
		if (r != l)
			break;
		r = r * bits_per_frame / 8;
		written += r;
		l = 0;
	}
	snd_pcm_nonblock(handle, 0);
	snd_pcm_drain(handle);
	snd_pcm_nonblock(handle, nonblock);
}



/*
 *  let's play or capture it (capture_type says VOC/WAVE/raw)
 */

static void playback(char *name)
{
	int ofs;
	size_t dta;
	ssize_t dtawave;
	int fd_0;

	pbrec_count = LLONG_MAX;
	fdcount = 0;
	if (!name || !strcmp(name, "-")) {
		fd_0 = fileno(stdin);
		name = "stdin";
	} else {
		init_stdin();
		if ((fd_0 = open(name, O_RDONLY, 0)) == -1) {
			perror(name);
			prg_exit(EXIT_FAILURE);
		}
	}
	printf("fd:%d\n",fd_0);
	debug_print
	/* read bytes for WAVE-header */
	//if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
	if (1) {
		dtawave = 0;
	printf("dtawave:%d\n",dtawave);
	hwparams.rate = 44100;
	hwparams.format = SND_PCM_FORMAT_S16_LE;
	hwparams.channels = 2;

		pbrec_count = calc_count();
		//playback_go(fd_0 ,dtawave, pbrec_count, FORMAT_WAVE, name);
	playback_go(fd_0 ,0, LLONG_MAX, FORMAT_WAVE, name);

	}
__end:
	if (fd_0 != 0)
		close(fd_0);
}


#endif



/**
 * mystrftime
 *
 *   Variant of strftime(3) that supports additional format
 *   specifiers in the format string.
 *
 * Parameters:
 *
 *   s	  - destination string
 *   max	- max number of bytes to write
 *   userformat - format string
 *   tm	 - time information
 *   filenumber - the number of the file, starting at 1
 *
 * Returns: number of bytes written to the string s
 */
size_t mystrftime(char *s, size_t max, const char *userformat,
		  const struct tm *tm, const int filenumber)
{
	char formatstring[PATH_MAX] = "";
	char tempstring[PATH_MAX] = "";
	char *format, *tempstr;
	const char *pos_userformat;

	format = formatstring;

	/* if mystrftime is called with userformat = NULL we return a zero length string */
	if (userformat == NULL) {
		*s = '\0';
		return 0;
	}

	for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
		if (*pos_userformat == '%') {
			tempstr = tempstring;
			tempstr[0] = '\0';
			switch (*++pos_userformat) {

				case '\0': // end of string
					--pos_userformat;
					break;

				case 'v': // file number 
					sprintf(tempstr, "%02d", filenumber);
					break;

				default: // All other codes will be handled by strftime
					*format++ = '%';
					*format++ = *pos_userformat;
					continue;
			}

			/* If a format specifier was found and used, copy the result. */
			if (tempstr[0]) {
				while ((*format = *tempstr++) != '\0')
					++format;
				continue;
			}
		}

		/* For any other character than % we simply copy the character */
		*format++ = *pos_userformat;
	}

	*format = '\0';
	format = formatstring;
	return strftime(s, max, format, tm);
}

static int new_capture_file(char *name, char *namebuf, size_t namelen,
			    int filecount)
{
	char *s;
	char buf[PATH_MAX+1];
	time_t t;
	struct tm *tmp;

	if (use_strftime) {
		t = time(NULL);
		tmp = localtime(&t);
		if (tmp == NULL) {
			perror("localtime");
			prg_exit(EXIT_FAILURE);
		}
		if (mystrftime(namebuf, namelen, name, tmp, filecount+1) == 0) {
			fprintf(stderr, "mystrftime returned 0");
			prg_exit(EXIT_FAILURE);
		}
		return filecount;
	}

	/* get a copy of the original filename */
	strncpy(buf, name, sizeof(buf));

	/* separate extension from filename */
	s = buf + strlen(buf);
	while (s > buf && *s != '.' && *s != '/')
		--s;
	if (*s == '.')
		*s++ = 0;
	else if (*s == '/')
		s = buf + strlen(buf);

	/* upon first jump to this if block rename the first file */
	if (filecount == 1) {
		if (*s)
			snprintf(namebuf, namelen, "%s-01.%s", buf, s);
		else
			snprintf(namebuf, namelen, "%s-01", buf);
		remove(namebuf);
		rename(name, namebuf);
		filecount = 2;
	}

	/* name of the current file */
	if (*s)
		snprintf(namebuf, namelen, "%s-%02i.%s", buf, filecount, s);
	else
		snprintf(namebuf, namelen, "%s-%02i", buf, filecount);

	return filecount;
}

/**
 * create_path
 *
 *   This function creates a file path, like mkdir -p. 
 *
 * Parameters:
 *
 *   path - the path to create
 *
 * Returns: 0 on success, -1 on failure
 * On failure, a message has been printed to stderr.
 */
int create_path(const char *path)
{
	char *start;
	mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;

	if (path[0] == '/')
		start = strchr(path + 1, '/');
	else
		start = strchr(path, '/');

	while (start) {
		char *buffer = strdup(path);
		buffer[start-path] = 0x00;

		if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
			perror(" ");
			free(buffer);
			return -1;
		}
		free(buffer);
		start = strchr(start + 1, '/');
	}
	return 0;
}

static int safe_open(const char *name)
{
	int fd;

	fd = open(name, O_WRONLY | O_CREAT, 0644);
	if (fd == -1) {
		if (errno != ENOENT || !use_strftime)
			return -1;
		if (create_path(name) == 0)
			fd = open(name, O_WRONLY | O_CREAT, 0644);
	}
	return fd;
}




